﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using AspNetMvc.Entities;
using AspNetMvc.Exceptions;
using AspNetMvc.Helpers;
using AspNetMvc.Models;
using AspNetMvc.Services.Events;
using AspNetMvc.Services.Events.Interfaces;
using CoreVirtuoso;
using CoreVirtuoso.Config;
using CoreVirtuoso.Descriptions;
using Infrastructure;
using Infrastructure.Events.Interfaces;
using Infrastructure.Lgd;
using Infrastructure.Pluralization;
using NGenerics.DataStructures;
using UsersProvider.Repositories;
using VDS.RDF;
using VirtuosoDal;
using VirtuosoDal.Config;
using VirtuosoDal.Entities;
using VirtuosoDal.Repositories.RdfRepositories.Events;
using VirtuosoDal.Repositories.RdfRepositories.GeoNames;
using VirtuosoDal.Repositories.RdfRepositories.Interfaces.Events;
using VirtuosoDal.Repositories.RdfRepositories.Lgd;
using WebGuiMvc.Models;
using WebGuiMvc.Helpers;
using WebGuiMvc.Helpers.Properties;
using UsersProvider.Repositories.RelationalRepositories;
using ViewType = AspNetMvc.Entities.EventViewType;

namespace AspNetMvc.Controllers
{
    public partial class EventController : BaseController
    {
        private readonly IEventsContentRepository _eventsContentRepository;
        private readonly IEventCommentsRepository _eventCommentsRepository;
        private readonly IEventCrudService _eventCrudService;
        private readonly IEventHoursListService _eventHoursListService;
        private readonly IEventMinutesListService _eventMinutesListService;
        private readonly IEventPostParser _eventPostParser;
        private readonly IEventsOntologyClassesService _eventsOntologyClassesService;
        private readonly IEventPropertyDescriptionManager _eventPropertyDescriptionManager;

        public EventController(IEventsContentRepository eventsContentRepository,
           IEventCommentsRepository eventCommentsRepository,
           IEventCrudService eventCrudService,
           IEventHoursListService eventHoursListService,
           IEventMinutesListService eventMinutesListService,
           IEventPostParser eventPostParser,
           IEventsOntologyClassesService eventsOntologyClassesService,
           IEventPropertyDescriptionManager eventPropertyDescriptionManager)
        {
            _eventsContentRepository = eventsContentRepository;
            _eventCommentsRepository = eventCommentsRepository;
            _eventCrudService = eventCrudService;
            _eventHoursListService = eventHoursListService;
            _eventMinutesListService = eventMinutesListService;
            _eventPostParser = eventPostParser;
            _eventsOntologyClassesService = eventsOntologyClassesService;
            _eventPropertyDescriptionManager = eventPropertyDescriptionManager;
        }

        [Authorize]
        public virtual ActionResult New()
        {
            var viewEvent = new EventForEditing(new Event())
                               {
                                   ViewType = EventViewType.Create,
                                   OperationName = ViewStrings.EventsStrings.CreateEvent,
                                   OperationButtonCaption = ViewStrings.EventsStrings.Create
                               };

            EventDateTimeListsHelper.FillForCreating(viewEvent, _eventHoursListService, _eventMinutesListService);

            EventTypeAndCategoriesListsHelper.Fill(viewEvent, _eventsOntologyClassesService, Language);

            return View(MVC.Event.Views.CreateOrEdit, viewEvent);
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public virtual JsonResult New(FormCollection collection)
        {
            try
            {
                var @event = _eventPostParser.Parse(collection);
                @event.Creator = UserName;

                var utcNow = DateTime.UtcNow;
                @event.CreationDateTime = utcNow;
                @event.LastEditingDateTime = utcNow;

                var eventId = _eventCrudService.CreateEvent(@event);

                return Json(new { state = "success", redirectUrl = String.Format("/event/successfullycreated/{0}", eventId) });
            }
            catch (ValidationException ex)
            {
                return Json(new { state = "error", errorMessage = HttpUtility.HtmlEncode(ex.Message) });
            }
            catch (Exception ex)
            {
                return Json(new { state = "error", errorMessage = HttpUtility.HtmlEncode("Event wasn't created. Try again later") });
            }
        }

        [Authorize]
        public virtual ActionResult Edit(int id)
        {
            string eventUri = Namespaces.EventsContentNamespace + id;
            var @event = _eventsContentRepository.GetEvent(eventUri, Language);

            var viewEvent = new EventForEditing(@event)
            {
                ViewType = ViewType.Edit,
                OperationName = ViewStrings.EventsStrings.EditEvent,
                OperationButtonCaption = ViewStrings.EventsStrings.Save
            };

            EventDateTimeListsHelper.FillForEditing(viewEvent, _eventHoursListService, _eventMinutesListService);

            EventTypeAndCategoriesListsHelper.Fill(viewEvent, _eventsOntologyClassesService, Language);

            @viewEvent.TypePropertiesForView = AdditionalPropertiesHelper.FillAdditionalPropertyWithValues(_eventPropertyDescriptionManager,
                                                                        @event.Type,
                                                                        @event.AdditionalPropertiesWithValues, Language);

            foreach (var category in @event.Categories)
                @viewEvent.CategoriesPropertiesForView[category] = AdditionalPropertiesHelper.FillAdditionalPropertyWithValues(
                        _eventPropertyDescriptionManager,
                        category,
                        @event.AdditionalPropertiesWithValues, Language);


            if (@event.Creator != UserName)
                //ToDo: implement roles based logic and redirection
                throw new Exception("This is not your event");

            return View(MVC.Event.Views.CreateOrEdit, viewEvent);
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public virtual JsonResult Edit(int id, FormCollection collection)
        {
            try
            {
                string eventUri = Namespaces.EventsContentNamespace + id;

                string eventCreator = _eventsContentRepository.GetEventCreator(eventUri);

                if (eventCreator != UserName)
                    //ToDo: implement roles based logic and redirection
                    throw new Exception("This is not your event");

                var @event = _eventPostParser.Parse(collection);
                @event.Uri = eventUri;

                DateTime utcNow = DateTime.UtcNow;
                @event.LastEditingDateTime = utcNow;

                _eventCrudService.EditEvent(@event);

                return Json(new { state = "success", redirectUrl = String.Format("/event/successfullyedited/{0}", id) });
            }
            catch (ValidationException ex)
            {
                return Json(new { state = "error", errorMessage = HttpUtility.HtmlEncode(ex.Message) });
            }
            catch (Exception ex)
            {
                return Json(new { state = "error", errorMessage = "Event wasn't edited. Try again later" });
            }
        }

        [Authorize]
        public virtual ActionResult MyEvents(int? page)
        {
            if (page == null)
                page = 1;

            var intPage = (int)page;

            ResultSet<Event> resultSet = _eventsContentRepository.GetUserEvents(this.User.Identity.Name,
               Language, (int)page, PagingConfig.PageSize);

            var eventsForView = EventForViewHelper.Build(_eventsOntologyClassesService, resultSet, Language);

            var pagedResultSet = new PagedModel<List<EventForView>>(eventsForView, resultSet.TotalCount, intPage, PagingConfig.PageSize);

            //ToDo: Move it to ResultSet under ItemsPluralizedName property
            ViewData["Events"] = new EventWordPluralizationFactory().GetPluralizationStrategy(CultureInfo.CurrentCulture).Pluralize(
                pagedResultSet.ItemsCount);

            return View(Views.MyEvents, pagedResultSet);
        }

        public virtual ActionResult FullDisplay(int id)
        {
            string eventUri = Namespaces.EventsContentNamespace + id;

            Event @event = _eventsContentRepository.GetEvent(eventUri, Language);
            IEnumerable<UsersProvider.Comments> comments = _eventCommentsRepository.GetComments(id);

            var eventForView = EventForViewHelper.Build(_eventsOntologyClassesService, @event, Language);
            eventForView.Comments = comments;

            EventForViewHelper.FillAdditionalProperties(eventForView, _eventsOntologyClassesService, _eventPropertyDescriptionManager, Language);

            return View(Views.FullDisplay, eventForView);
        }

        [Authorize]
        public virtual ActionResult PostComment(int eventId, string comment)
        {
            (new EventCommentsRepository()).PostComment(eventId, User.Identity.Name, comment);
            return RedirectToRoute("EventFullDisplay", new { id = eventId });
        }

        //ToDo: Reimplement: store id at user's session
        [Authorize]
        public virtual ViewResult SuccessfullyCreated(int id)
        {
            this.ViewData["EventId"] = id;

            return View((string)Views.SuccessfullyCreated);
        }

        //ToDo: Reimplement: store id at user's session
        [Authorize]
        public virtual ViewResult SuccessfullyEdited(int id)
        {
            this.ViewData["EventId"] = id;

            return View((string)Views.SuccessfullyEdited);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public virtual JsonResult GetCities(string query)
        {
            GeoNamesRepository geoRepository = new GeoNamesRepository();
            IEnumerable<CityDescription> citiesDescriptions = geoRepository.GetCities(query, Language);

            return Json(citiesDescriptions);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public virtual JsonResult GetGeoObjects(string query)
        {
            GeoNamesRepository geoNamesRepository = new GeoNamesRepository();
            IEnumerable<GeoNamesObjectDescription> geoNamesObjectDescriptions = geoNamesRepository.GetGeoObjects(query,
                                                                                                                 (string)
                                                                                                                 Session[
                                                                                                                    "Language"
                                                                                                                    ]);
            return Json(geoNamesObjectDescriptions.Select(gnod =>
                                                             {
                                                                 string geoObjectTypePrefix = "";
                                                                 if (gnod.GeoObjectType == GeoObjectType.City)
                                                                     geoObjectTypePrefix = ViewStrings.EventsStrings.City + ": ";
                                                                 else if (gnod.GeoObjectType == GeoObjectType.Country)
                                                                     geoObjectTypePrefix = ViewStrings.EventsStrings.Country + ": ";

                                                                 return new
                                                                           {
                                                                               Uri = gnod.Uri,
                                                                               Name =
                                                                                  String.Format("{0}{1}", geoObjectTypePrefix,
                                                                                                gnod.GeoObjectName)
                                                                           };
                                                             }));
        }

        [Authorize]
        public virtual PartialViewResult AddNewPlace()
        {
            return PartialView();
        }

        [Authorize]
        public virtual PartialViewResult AddLabeledPlace()
        {
            GeoLabelsRepository geoLabelsRepository = new GeoLabelsRepository();
            this.ViewData["GeoLabelsDescriptions"] = geoLabelsRepository.GetGeoLabels(this.User.Identity.Name);

            return PartialView("LabeledPlaces");
        }



        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public virtual JsonResult GetNearObjects(string objectTypeUri, string westLatitude, string southLongitude,
           string eastLatitude, string northLongitude)
        {
            //We can't use double parameters, because they will be parsed using current culture (that may have non-point delimeter)
            double doubleWestLatitude = Double.Parse(westLatitude, CultureInfo.InvariantCulture);
            double doubleSouthLongitude = Double.Parse(southLongitude, CultureInfo.InvariantCulture);
            double doubleEastLatitude = Double.Parse(eastLatitude, CultureInfo.InvariantCulture);
            double doubleNorthLongitude = Double.Parse(northLongitude, CultureInfo.InvariantCulture);

            LgdContentRepository geoRepository = new LgdContentRepository();
            IEnumerable<GeoObjectDescription> nearestObjectsByType = geoRepository.GetNearestObjectsByType(objectTypeUri,
              doubleWestLatitude, doubleSouthLongitude,
               doubleEastLatitude, doubleNorthLongitude);

            return Json(nearestObjectsByType);
        }

        [Authorize]
        [AcceptVerbs(HttpVerbs.Get)]
        public void UpdateGeoLabelLastUsageDateTime(int labelId)
        {
            string userName = this.User.Identity.Name;

            GeoLabelsRepository geoLabelsRepository = new GeoLabelsRepository();
            geoLabelsRepository.UpdateGeoLabelLastUsageDateTime(labelId, DateTimeOffset.UtcNow, userName);
        }

        public virtual PartialViewResult GetTypeAdditionalProperties(string type)
        {
            string typeUri = String.Format("{0}{1}", Namespaces.EventsOntologyNamespace, type);

            var propertiesWithValues = AdditionalPropertiesHelper.FillAdditionalPropertyWithValues(_eventPropertyDescriptionManager, 
                typeUri, null, Language);

            var propertiesSet = new ViewPropertiesSet(EventViewType.Create, propertiesWithValues);

            return PartialView("EventsMarkup/DynamicProperties/EditableDynamicProperties", propertiesSet);
        }

        public virtual PartialViewResult GetCategoryAdditionalProperties(string category)
        {
            string categoryUri = String.Format("{0}{1}", Namespaces.EventsOntologyNamespace, category);

            var propertiesWithValues =
                AdditionalPropertiesHelper.FillAdditionalPropertyWithValues(_eventPropertyDescriptionManager,
                                                                            categoryUri, null, Language);

            var propertiesSet = new ViewPropertiesSet(EventViewType.Create, propertiesWithValues);

            return PartialView("EventsMarkup/DynamicProperties/EditableDynamicProperties", propertiesSet);
        }
    }
}
